//
//  CXBtCore.m
//  JwPack
//
//  Created by 陈警卫 on 2025/8/12.
//

#import "CXBtCore.h"

@interface CXBtCore ()<CBCentralManagerDelegate, CBPeripheralDelegate>

/// 中心设备
@property (nonatomic, strong) CBCentralManager *centralManager;

/// 外设数组
/// 数组中只有 state:
/// CXBtPeriStateConnecting
/// CXBtPeriStateConnected
/// CXBtPeriStateNotifyed
@property (nonatomic, strong) NSMutableArray<CXBtPeriModel *> *periModels;

/// 扫描自动连接数组
@property (nonatomic, strong) NSMutableArray<CXBtScanModel *> *scanModels;

/// 配置合并
@property (nonatomic, strong) CXBtConfig *configMerge;

/// 信号计时器
@property (nonatomic, strong) NSTimer *timerRssi;

@end

@implementation CXBtCore

#pragma mark - init

/// 初始化
- (instancetype)initWithConfig:(CXBtConfig *)config {
    self = [super init];
    if (self) {
        self.config = config;
        self.configMerge = [self.config copy];
        [self setupData];
    }
    return self;
}

/// 参数配置
- (void)setupData {
    self.periModels = [NSMutableArray array];
    self.scanModels = [NSMutableArray array];
    
    NSMutableDictionary *options = [NSMutableDictionary dictionary];
    [options addEntriesFromDictionary:@{CBCentralManagerOptionShowPowerAlertKey: [NSNumber numberWithBool:self.configMerge.showPowerAlertKey]}];
    self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:dispatch_get_global_queue(0, 0) options:options];
}

/// 过滤配置
- (void)setupFilt {
    self.configMerge = [self.config copy];
    
    for (CXBtScanModel *scanModel in self.scanModels) {
        if (scanModel.connectName) {
            [self.configMerge.filtAdvNames addObject:scanModel.connectName];
        }
        if (scanModel.connectUuid) {
            [self.configMerge.filtAdvPeriUUIDs addObject:[CBUUID UUIDWithString:scanModel.connectUuid]];
        }
        // 不启用
        /**
         if (scanModel.serviceUUIDs.count > 0) {
             [self.configMerge.scanUUIDs addObjectsFromArray:scanModel.serviceUUIDs.allObjects];
             [self.configMerge.filtAdvServiceUUIDs addObjectsFromArray:scanModel.serviceUUIDs.allObjects];
         }
         */        
    }
}

#pragma mark - scan

/// 扫描
- (void)scan {
    [self stopScan];
    [self setupFilt];
    
    NSMutableDictionary *options = [NSMutableDictionary dictionary];
    [options addEntriesFromDictionary:@{CBCentralManagerScanOptionAllowDuplicatesKey: [NSNumber numberWithBool:self.configMerge.allowDuplicatesKey]}];
    NSArray *scanUUIDs = [self.configMerge.scanUUIDs allObjects];
    if (scanUUIDs.count <= 0 ) {
        scanUUIDs = nil;
    }
    [self.centralManager scanForPeripheralsWithServices:scanUUIDs options:options];
}

/// 停止扫描
- (void)stopScan {
    [self.centralManager stopScan];
}

#pragma mark - connect

/// 连接
- (void)connectPeriModel:(CXBtPeriModel *)periModel {
    if (periModel.peri) {
        CXBtPeriModel *exPeriModel = [self periModelsWithPeri:periModel.peri];
        if (exPeriModel.state == CXBtPeriStateConnecting) {
            // 正在连接的不重复连接
            return;
        }
        // 连接前持有
        periModel.state = CXBtPeriStateConnecting;
        [self addResetPeriModels:periModel];
        [self.centralManager connectPeripheral:periModel.peri options:nil];
    }
}

/// 断开连接
- (void)disconnectPeriModel:(CXBtPeriModel *)periModel {
    if (periModel.peri) {
        // 先删除缓存对象
        [self removePeriModels:periModel];
        [self.centralManager cancelPeripheralConnection:periModel.peri];
    }
}

/// 断开连接
- (void)disconnectAll {
    NSMutableArray *periModels = [NSMutableArray arrayWithArray:self.periModels];
    for (CXBtPeriModel *model in periModels) {
        [self disconnectPeriModel:model];
    }
}

/// 通过外设UUID恢复
- (CBPeripheral *)retrievePeripheralsWithUuid:(NSString *)uuid {
    if (uuid) {
        NSUUID *UUID = [[NSUUID alloc] initWithUUIDString:uuid];
        NSArray *peris = [self.centralManager retrievePeripheralsWithIdentifiers:@[UUID]];
        if (peris.count > 0) {
            return peris.firstObject;
        }
    }
    return nil;
}

#pragma mark - notify

/// 自动连接订阅外设
/// peri 传入:直接连接 不传:则通过scanModel连接
- (void)autoNotifyPeriModel:(CXBtPeriModel *)periModel comple:(void(^)(NSError *error))comple {
    if (periModel.peri) {
        // 直接连接
        [self connectPeriModel:periModel];
    } else {
        CXBtScanModel *scanModel = periModel.scanModel;
        if (scanModel.connectUuid.length <= 0 && scanModel.connectName.length <= 0) {
            NSString *domain = @"notifyPeriModel--(connectUuid & connectName)为nil";
            kCXBtLog(@"%@", domain);
            NSError *error = [NSError errorWithDomain:domain code:0 userInfo:nil];
            if (comple) {
                comple(error);
            }
            return;
        }
        
        BOOL isScan = YES;
        if (periModel.isRetrieve && scanModel.connectUuid) {
            CBPeripheral *peri = [self retrievePeripheralsWithUuid:scanModel.connectUuid];
            if (peri) {
                isScan = NO;
                // 恢复连接
                [self connectPeriModel:periModel];
            }
        }
        if (isScan) {
            // 扫描连接
            [self addResetScanModels:scanModel];
            [self scan];
        }
    }
    periModel.didTimeoutAction = comple;
    [periModel startTimer];
}

#pragma mark - handle

/// 添加扫描自动连接外设
/// 添加后重新扫描
- (void)addAutoConnectScanModel:(CXBtScanModel *)scanModel {
    if (!scanModel.connectUuid && !scanModel.connectName) {
        return;
    }
    [self addResetScanModels:scanModel];
}

#pragma mark - handle periModels

/// 获取缓存外设模型
- (CXBtPeriModel *)periModelsWithPeri:(CBPeripheral *)peri {
    return [self periModelsWithUuid:peri.identifier.UUIDString];
}

/// 获取缓存外设模型
- (CXBtPeriModel *)periModelsWithUuid:(NSString *)uuid {
    CXBtPeriModel *periModel = nil;
    for (CXBtPeriModel *model in self.periModels) {
        if ([model.periUuid isEqualToString:uuid]) {
            periModel = model;
            break;
        }
    }
    return periModel;
}

/// 重置添加缓存外设模型
- (void)addResetPeriModels:(CXBtPeriModel *)periModel {
    [self removePeriModels:periModel];
    [self.periModels addObject:periModel];
}

/// 删除缓存外设模型
- (void)removePeriModels:(CXBtPeriModel *)periModel {
    NSString *uuid = periModel.peri.identifier.UUIDString;
    [self.periModels enumerateObjectsWithOptions:(NSEnumerationReverse) usingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        CXBtPeriModel *model = (CXBtPeriModel *)obj;
        if ([model.periUuid isEqualToString:uuid]) {
            [self.periModels removeObject:obj];
        }
    }];
}

#pragma mark - handle scanModels

/// 获取扫描连接模型
- (CXBtScanModel *)scanModelsWithUuid:(NSString *)uuid {
    CXBtScanModel *scanModel = nil;
    for (CXBtScanModel *model in self.scanModels) {
        if ([model.connectUuid isEqualToString:uuid]) {
            scanModel = model;
            break;
        }
    }
    return scanModel;
}

/// 获取扫描连接模型
- (CXBtScanModel *)scanModelsWithName:(NSString *)name {
    CXBtScanModel *scanModel = nil;
    for (CXBtScanModel *model in self.scanModels) {
        if ([model.connectName isEqualToString:name]) {
            scanModel = model;
            break;
        }
    }
    return scanModel;
}

/// 删除扫描连接模型
- (void)removeScanModels:(CXBtScanModel *)scanModel {
    [self.scanModels enumerateObjectsWithOptions:(NSEnumerationReverse) usingBlock:^(CXBtScanModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        CXBtScanModel *model = (CXBtScanModel *)obj;
        if (scanModel.connectName && [scanModel.connectName isEqualToString:model.connectName]) {
            [self.scanModels removeObject:obj];
        }
        if (scanModel.connectUuid && [scanModel.connectUuid isEqualToString:model.connectUuid]) {
            [self.scanModels removeObject:obj];
        }
    }];
    if (self.scanModels.count == 0) {
        // 停止扫描
        [self stopScan];
    }
}

/// 重置添加扫描连接模型
- (void)addResetScanModels:(CXBtScanModel *)scanModel {
    // 判断是否已经是正在连接流程的设备
    BOOL isContained = NO;
    for (CXBtPeriModel *periModel in self.periModels) {
        if ([periModel.periName isEqualToString:scanModel.connectName] ||
            [periModel.periUuid isEqualToString:scanModel.connectUuid] ) {
            if (periModel.state == CXBtPeriStateConnecting ||
                periModel.state == CXBtPeriStateConnected ||
                periModel.state == CXBtPeriStateNotifyed) {
                isContained = YES;
                break;
            }
        }
    }
    if (isContained) {
        return;
    }
    [self removeScanModels:scanModel];
    [self.scanModels addObject:scanModel];
}

#pragma mark - CBCentralManagerDelegate

// 状态回调
- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
    CBManagerState state = central.state;
    if (state == CBManagerStateUnknown) {
        self.state = CXBtStateUnsupported;
    } else if (state == CBManagerStateResetting) {
        self.state = CXBtStateUnsupported;
    } else if (state == CBManagerStateUnsupported) {
        self.state = CXBtStateUnsupported;
    } else if (state == CBManagerStateUnauthorized) {
        self.state = CXBtStateUnauthorized;
    } else if (state == CBManagerStatePoweredOff) {
        self.state = CXBtStatePoweredOff;
    } else if (state == CBManagerStatePoweredOn) {
        self.state = CXBtStatePoweredOn;
    }
}

// 扫描回调
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI {
    // 无名称过滤
    if (peripheral.name.length <= 0) {
        return;
    }
    //kCXBtLog(@"扫描外设--%@", peripheral);
    BOOL isContain = NO;
    // 外设广播服务UUID过滤
    NSArray *advUUIDs = [advertisementData objectForKey:CBAdvertisementDataServiceUUIDsKey];
    if (self.configMerge.filtAdvServiceUUIDs.count > 0) {
        for (CBUUID *uuid in advUUIDs) {
            if ([self.configMerge.filtAdvServiceUUIDs containsObject:uuid]) {
                isContain = YES;
                break;
            }
        }
    }
    // 外设名称过滤
    if (self.configMerge.filtAdvNames.count > 0) {
        for (NSString *name in self.configMerge.filtAdvNames) {
            if ([peripheral.name containsString:name]) {
                isContain = YES;
                break;
            }
        }
    }
    // 外设UUID过滤
    if (self.configMerge.filtAdvPeriUUIDs.count > 0) {
        for (CBUUID *uuid in self.configMerge.filtAdvPeriUUIDs) {
            if ([peripheral.identifier isEqual:uuid]) {
                isContain = YES;
                break;
            }
        }
    }
    if (isContain) {
        kCXBtLog(@"过滤扫描外设--%@--%@", peripheral, advertisementData);
        NSString *uuid = peripheral.identifier.UUIDString;
        NSString *name = peripheral.name;
        
        CXBtPeriModel *periModel = [[CXBtPeriModel alloc] init];
        periModel.peri = peripheral;
        periModel.advDict = advertisementData;
        periModel.rssi = RSSI;
        // 扫描回调
        periModel.state = CXBtPeriStateScaned;
        if (self.didScanedPeri) {
            self.didScanedPeri(periModel);
        }
        // 扫描连接
        CXBtScanModel *scanModel = [self scanModelsWithUuid:uuid];
        if (!scanModel) {
            scanModel = [self scanModelsWithName:name];
        }
        if (scanModel) {
            periModel.scanModel = scanModel;
            [self connectPeriModel:periModel];
        }
    }
}

// 连接回调
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
    kCXBtLog(@"连接成功--%@", peripheral);
    CXBtPeriModel *periModel = [self periModelsWithPeri:peripheral];
    periModel.peri = peripheral;
    periModel.state = CXBtPeriStateConnected;
    // 连接回调
    if (self.didConncetedPeri) {
        self.didConncetedPeri(periModel);
    }
    [self addResetPeriModels:periModel];
    
    NSArray *serviceUUIDs = periModel.scanModel.serviceUUIDs.allObjects;
    if (serviceUUIDs.count <= 0) {
        serviceUUIDs = nil;
    }
    peripheral.delegate = self;
    // 判断是否需要发现服务
    if (periModel.scanModel.writeUUIDs.count > 0 || periModel.scanModel.notifUUIDs.count > 0) {
        [peripheral discoverServices:serviceUUIDs];
    }
    [self removeScanModels:periModel.scanModel];
}

// 连接失败回调
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
    kCXBtLog(@"连接失败--%@--%@", peripheral, error);
    CXBtPeriModel *periModel = [self periModelsWithPeri:peripheral];
    if (!periModel) {
        periModel = [[CXBtPeriModel alloc] init];
    }
    periModel.peri = peripheral;
    [self removePeriModels:periModel];
    periModel.state = CXBtPeriStateConnectFail;
    // 连接失败回调
    if (self.didConncetFailPeri) {
        self.didConncetFailPeri(periModel);
    }
}

// 断开连接回调
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
    kCXBtLog(@"断开连接--%@--%@", peripheral, error);
    CXBtPeriModel *periModel = [self periModelsWithPeri:peripheral];
    if (!periModel) {
        periModel = [[CXBtPeriModel alloc] init];
    }
    periModel.peri = peripheral;
    [self removePeriModels:periModel];
    periModel.state = CXBtPeriStateDisconnected;
    // 断开连接回调
    if (self.didDisconncetPeri) {
        self.didDisconncetPeri(periModel);
    }
    
    if (periModel.isAutoReconnect) {
        // 被动断开自动复连
        // 主动断开时 会先删除缓存数据 恢复不到scanModel 不能复连
        if (periModel.scanModel.serviceUUIDs.count <= 0) {
            return;
        }
        periModel.scanModel.connectUuid = periModel.periUuid;
        [self addResetScanModels:periModel.scanModel];
        [self scan];
    }
}

#pragma mark - CBPeripheralDelegate

// 发现服务
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
    kCXBtLog(@"发现服务--%@--%@", peripheral, peripheral.services);
    CXBtPeriModel *periModel = [self periModelsWithPeri:peripheral];
    periModel.peri = peripheral;
    
    NSMutableArray *charaUuids = [NSMutableArray array];
    [charaUuids addObjectsFromArray:periModel.scanModel.writeUUIDs.allObjects];
    [charaUuids addObjectsFromArray:periModel.scanModel.notifUUIDs.allObjects];
    if (charaUuids.count <= 0) {
        charaUuids = nil;
    }
    BOOL isSuccess = NO;
    for (CBService *service in peripheral.services) {
        if ([periModel.scanModel.serviceUUIDs containsObject:service.UUID]) {
            isSuccess = YES;
            [peripheral discoverCharacteristics:charaUuids forService:service];
        }
    }
    if (!isSuccess || error) {
        [self disconnectPeriModel:periModel];
        kCXBtLog(@"发现服务失败--%@--%@", peripheral, error);
    }
}

// 发现特征
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
    kCXBtLog(@"发现特征--%@--%@--%@", peripheral, service, service.characteristics);
    CXBtPeriModel *periModel = [self periModelsWithPeri:peripheral];
    periModel.peri = peripheral;
    
    BOOL isSuccess_write = NO;
    BOOL isSuccess_notif = NO;
    for (CBCharacteristic *chara in service.characteristics) {
        if ([periModel.scanModel.writeUUIDs containsObject:chara.UUID]) {
            kCXBtLog(@"发现写入特征--%@", chara);
            isSuccess_write = YES;
            periModel.writeChara = chara;
        }
        if ([periModel.scanModel.notifUUIDs containsObject:chara.UUID]) {
            kCXBtLog(@"发现订阅特征--%@", chara);
            isSuccess_notif = YES;
            periModel.notifChara = chara;
            [peripheral setNotifyValue:YES forCharacteristic:chara];
        }
    }
    if (periModel.scanModel.writeUUIDs.count > 0 && !isSuccess_write) {
        kCXBtLog(@"发现写入特征失败--%@--%@", service, error);
        [self disconnectPeriModel:periModel];
    }
    if (periModel.scanModel.notifUUIDs.count > 0 && !isSuccess_notif) {
        kCXBtLog(@"发现订阅特征失败--%@--%@", service, error);
        [self disconnectPeriModel:periModel];
    }
    if (error) {
        kCXBtLog(@"发现特征失败--%@--%@", service, error);
        [self disconnectPeriModel:periModel];
    }
}

// 订阅状态回调
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
    kCXBtLog(@"订阅回调--%@--%@", peripheral, characteristic);
    
    BOOL isSuccess = NO;
    CXBtPeriModel *periModel = [self periModelsWithPeri:peripheral];
    if (characteristic.isNotifying) {
        if ([periModel.scanModel.notifUUIDs containsObject:characteristic.UUID]) {
            kCXBtLog(@"订阅成功--%@--%@", peripheral, characteristic);
            isSuccess = YES;
            
            periModel.state = CXBtPeriStateNotifyed;
            periModel.peri = peripheral;
            periModel.notifChara = characteristic;
            [self addResetPeriModels:periModel];
            
            // 订阅回调
            if (self.didNotifedPeri) {
                self.didNotifedPeri(periModel);
            }
        }
    }
    if (!isSuccess || error) {
        kCXBtLog(@"订阅失败---%@---%@", characteristic, error);
        [self disconnectPeriModel:periModel];
    }
}

// 订阅数据回调
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
    //kCXBtLog(@"订阅数据回调--%@--%@", peripheral, characteristic);
    
    CXBtPeriModel *periModel = [self periModelsWithPeri:peripheral];
    periModel.peri = peripheral;
    
    if ([periModel.scanModel.notifUUIDs containsObject:characteristic.UUID]) {
        NSData *data = characteristic.value;
        // 订阅数据回调
        if (self.didNotifedValue) {
            self.didNotifedValue(periModel, data);
        }
    }
}

// 信号回调
- (void)peripheral:(CBPeripheral *)peripheral didReadRSSI:(NSNumber *)RSSI error:(NSError *)error {
    CXBtPeriModel *periModel = [self periModelsWithPeri:peripheral];
    periModel.rssi = RSSI;
    if (self.didRssiPeri) {
        self.didRssiPeri(periModel);
    }
}

#pragma mark - tool

/// 是否连接外设(包括订阅)
- (BOOL)isConnectedUuid:(NSString *)uuid {
    BOOL isConnected = NO;
    for (CXBtPeriModel *periModel in self.periModels) {
        if ([periModel.periUuid isEqualToString:uuid]) {
            if (periModel.state == CXBtPeriStateConnected || periModel.state == CXBtPeriStateNotifyed) {
                isConnected = YES;
                break;
            }
        }
    }
    return isConnected;
}

/// 是否连接外设(包括订阅)
- (BOOL)isConnectedName:(NSString *)name {
    BOOL isConnected = NO;
    for (CXBtPeriModel *periModel in self.periModels) {
        if ([periModel.periName isEqualToString:name]) {
            if (periModel.state == CXBtPeriStateConnected || periModel.state == CXBtPeriStateNotifyed) {
                isConnected = YES;
                break;
            }
        }
    }
    return isConnected;
}

/// 是否订阅外设
- (BOOL)isNotifyedUuid:(NSString *)uuid {
    BOOL isNotifyed = NO;
    for (CXBtPeriModel *periModel in self.periModels) {
        if ([periModel.periUuid isEqualToString:uuid] && periModel.state == CXBtPeriStateNotifyed) {
            isNotifyed = YES;
            break;
        }
    }
    return isNotifyed;
}

/// 是否订阅外设
- (BOOL)isNotifyedName:(NSString *)name {
    BOOL isNotifyed = NO;
    for (CXBtPeriModel *periModel in self.periModels) {
        if ([periModel.periName isEqualToString:name] && periModel.state == CXBtPeriStateNotifyed) {
            isNotifyed = YES;
            break;
        }
    }
    return isNotifyed;
}

/// 已订阅的外设数组
- (NSArray<CXBtPeriModel *> *)notifedPeriModels {
    NSMutableArray *periModels = [NSMutableArray array];
    for (CXBtPeriModel *model in self.periModels) {
        if (model.state == CXBtPeriStateNotifyed) {
            [periModels addObject:model];
        }
    }
    return periModels;
}

#pragma mark - property

- (void)setState:(CXBtState)state {
    if (_state == CXBtStatePoweredOff && state == CXBtStatePoweredOn) {
        // 自动连接
        if (self.configMerge.disconnectReconnect) {
            [self scan];
        }
    }
    _state = state;
    if (self.didState) {
        self.didState(state);
    }
}

#pragma mark - func

/// 开始读取外设信号计时器
- (void)startTimerRssiWithInterval:(NSTimeInterval)interval {
    [self endTimerRssi];
    __weak typeof(self) wself = self;
    self.timerRssi = [NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
        [wself timerRssiAction];
    }];
    // 判断当前是否主线程
    if (![NSThread isMainThread]) {
        [[NSRunLoop currentRunLoop] addTimer:self.timerRssi forMode:NSRunLoopCommonModes];
        [[NSRunLoop currentRunLoop] run];
    }
}

/// 结束读取外设信号计时器
- (void)endTimerRssi {
    [self.timerRssi invalidate];
    self.timerRssi = nil;
}

/// 读取外设信号
- (void)timerRssiAction {
    if (self.state == CXBtStatePoweredOn) {
        for (CXBtPeriModel *model in self.periModels) {
            [model.peri readRSSI];
        }
    }
}

@end
